#!/usr/bin/env python
# coding: utf-8

# In[1]:


import hashlib, json, sys

def hashMe(msg=""):
    #为了方便起见，这是一个包装哈希算法的助手函数
    if type(msg)!=str:
        msg = json.dumps(msg,sort_keys=True)  #如果我们不给密钥排序，就不能保证重复性！
        
    if sys.version_info.major == 2:
        return unicode(hashlib.sha256(msg).hexdigest(),'utf-8')
    else:
        return hashlib.sha256(str(msg).encode('utf-8')).hexdigest()


# In[2]:


import random
random.seed(0)

def makeTransaction(maxValue=3):
    #这将创建范围为（1，maxValue）的有效交易
    sign      = int(random.getrandbits(1))*2 - 1   #这将随机选择-1或1
    amount    = random.randint(1,maxValue)
    alicePays = sign * amount
    bobPays   = -1 * alicePays
    #通过构造，这将始终返回尊重令牌保护的交易。
    #不过，请注意，我们没有做任何事情来检查这些透支帐户是否
    return {u'Alice':alicePays,u'Bob':bobPays}


# In[3]:


txnBuffer = [makeTransaction() for i in range(30)]


# In[4]:


def updateState(txn, state):
    #输入：txn，state：输入账户名称的字典，保存转账金额（txn）或账户余额（state）的数值
    #返回：Updated state，如果需要，可以向state添加其他用户
    #注意：这不会验证事务-只是更新状态！
    #如果事务有效，则更新状态
    state = state.copy() #由于字典是可变的，所以让我们通过创建数据的工作副本来避免任何混淆。
    for key in txn:
        if key in state.keys():
            state[key] += txn[key]
        else:
            state[key] = txn[key]
    return state


# In[5]:


def isValidTxn(txn,state):
    #假设交易是由帐户名键入的字典
    #检查存款和取款的总和是否为0
    if sum(txn.values()) is not 0:
        return False
    
    #检查交易是否导致透支
    for key in txn.keys():
        if key in state.keys(): 
            acctBalance = state[key]
        else:
            acctBalance = 0
        if (acctBalance + txn[key]) < 0:
            return False
    
    return True


# In[6]:


state = {u'Alice':5,u'Bob':5}

print(isValidTxn({u'Alice': -3, u'Bob': 3},state))  #基本交易-这太好了！
print(isValidTxn({u'Alice': -4, u'Bob': 3},state))  #但我们不能创建或销毁代币！
print(isValidTxn({u'Alice': -6, u'Bob': 6},state))  #我们也不能透支我们的帐户。
print(isValidTxn({u'Alice': -4, u'Bob': 2,'Lisa':2},state)) #创建新用户有效
print(isValidTxn({u'Alice': -4, u'Bob': 3,'Lisa':2},state)) #但同样的规则仍然适用！


# In[7]:


state = {u'Alice':50, u'Bob':50}  #定义初始状态
genesisBlockTxns = [state]
genesisBlockContents = {u'blockNumber':0,u'parentHash':None,u'txnCount':1,u'txns':genesisBlockTxns}
genesisHash = hashMe( genesisBlockContents )
genesisBlock = {u'hash':genesisHash,u'contents':genesisBlockContents}
genesisBlockStr = json.dumps(genesisBlock, sort_keys=True)


# In[8]:


chain = [genesisBlock]


# In[9]:


def makeBlock(txns,chain):
    parentBlock = chain[-1]
    parentHash  = parentBlock[u'hash']
    blockNumber = parentBlock[u'contents'][u'blockNumber'] + 1
    txnCount    = len(txns)
    blockContents = {u'blockNumber':blockNumber,u'parentHash':parentHash,
                     u'txnCount':len(txns),'txns':txns}
    blockHash = hashMe( blockContents )
    block = {u'hash':blockHash,u'contents':blockContents}
    
    return block


# In[10]:

#每个块的任意交易数
#这是由块矿工选择的，并且可以在块之间变化！
blockSizeLimit = 5  

while len(txnBuffer) > 0:
    bufferStartSize = len(txnBuffer)
    
    #收集要包含的一组有效交易
    txnList = []
    while (len(txnBuffer) > 0) & (len(txnList) < blockSizeLimit):
        newTxn = txnBuffer.pop()
        validTxn = isValidTxn(newTxn,state) 
        
        if validTxn:           #如果我们有一个有效的状态，不是“假”
            txnList.append(newTxn)
            state = updateState(newTxn,state)
        else:
            print("ignored transaction")
            sys.stdout.flush()
            continue  #这是一个无效的交易；请忽略它并继续
        
    ##制造障碍
    myBlock = makeBlock(txnList,chain)
    chain.append(myBlock) 


# In[11]:


chain[0]


# In[12]:


chain[1]


# In[13]:


state


# In[14]:


def checkBlockHash(block):
    #如果哈希与块内容不匹配，则引发异常
    expectedHash = hashMe( block['contents'] )
    if block['hash']!=expectedHash:
        raise Exception('Hash does not match contents of block %s'%
                        block['contents']['blockNumber'])
    return


# In[15]:


def checkBlockValidity(block,parent,state):    
    #我们要检查以下情况：
    #每个事务都是对系统状态的有效更新
    #块哈希对块内容有效
    #Block number将父块编号递增1
    #准确引用父块的哈希
    parentNumber = parent['contents']['blockNumber']
    parentHash   = parent['hash']
    blockNumber  = block['contents']['blockNumber']
    
    #检查事务有效性；如果发现无效事务，则抛出错误。
    for txn in block['contents']['txns']:
        if isValidTxn(txn,state):
            state = updateState(txn,state)
        else:
            raise Exception('Invalid transaction in block %s: %s'%(blockNumber,txn))

    checkBlockHash(block) #检查哈希完整性；如果不准确则引发错误

    if blockNumber!=(parentNumber+1):
        raise Exception('Hash does not match contents of block %s'%blockNumber)

    if block['contents']['parentHash'] != parentHash:
        raise Exception('Parent hash not accurate at block %s'%blockNumber)
    
    return state


# In[16]:


def checkChain(chain):
    #从创世大厦的链条中穿行（得到特殊处理），
    #检查所有事务是否在内部有效，
    #交易不会导致透支，
    #这些块是通过散列连接的。
    #它将状态作为账户和余额的字典返回，
    #如果检测到错误，则返回False

    
    ##数据输入处理：确保我们的链是一个dict列表
    if type(chain)==str:
        try:
            chain = json.loads(chain)
            assert( type(chain)==list)
        except:  #这是一个包罗万象，诚然很粗糙
            return False
    elif type(chain)!=list:
        return False
    
    state = {}
    
    ##通过检查创世区块来充注泵
    #我们要检查以下情况：
    #每个事务都是对系统状态的有效更新
    #块哈希对块内容有效

    for txn in chain[0]['contents']['txns']:
        state = updateState(txn,state)
    checkBlockHash(chain[0])
    parent = chain[0]
    
    ##检查后续块：还需要检查这些块
    #对父块哈希的引用
    #区块编号的有效性
    for block in chain[1:]:
        state = checkBlockValidity(block,parent,state)
        parent = block
        
    return state


# In[17]:


checkChain(chain)


# In[21]:


chainAsText = json.dumps(chain,sort_keys=True)
checkChain(chainAsText)


# In[22]:


import copy
nodeBchain = copy.copy(chain)
nodeBtxns  = [makeTransaction() for i in range(5)]
newBlock   = makeBlock(nodeBtxns,nodeBchain)


# In[23]:


print("Blockchain on Node A is currently %s blocks long"%len(chain))

try:
    print("New Block Received; checking validity...")
    state = checkBlockValidity(newBlock,chain[-1],state) #如果此错误阻止更新，则将引发错误！
    chain.append(newBlock)
except:
    print("Invalid block; ignoring and waiting for the next block...")

print("Blockchain on Node A is now %s blocks long"%len(chain))





